package org.unidata.mdm.rest.v1.dq.core.converter;

import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.unidata.mdm.core.util.SecurityUtils;
import org.unidata.mdm.dq.core.dto.GetGroupsModelResult;
import org.unidata.mdm.dq.core.type.model.source.CleanseFunctionGroup;
import org.unidata.mdm.dq.core.type.model.source.CompositeCleanseFunctionSource;
import org.unidata.mdm.dq.core.type.model.source.GroovyCleanseFunctionSource;
import org.unidata.mdm.dq.core.type.model.source.JavaCleanseFunctionSource;
import org.unidata.mdm.dq.core.type.model.source.PythonCleanseFunctionSource;
import org.unidata.mdm.dq.core.util.DQUtils;
import org.unidata.mdm.rest.system.converter.Converter;
import org.unidata.mdm.rest.system.converter.QuadConverter;
import org.unidata.mdm.rest.v1.dq.core.ro.functions.CleanseFunctionGroupInfoRO;
import org.unidata.mdm.rest.v1.dq.core.ro.functions.CleanseFunctionGroupRO;
import org.unidata.mdm.rest.v1.dq.core.ro.functions.CompositeCleanseFunctionRO;
import org.unidata.mdm.rest.v1.dq.core.ro.functions.GroovyCleanseFunctionRO;
import org.unidata.mdm.rest.v1.dq.core.ro.functions.JavaCleanseFunctionRO;
import org.unidata.mdm.rest.v1.dq.core.ro.functions.PythonCleanseFunctionRO;

/**
 * @author Mikhail Mikhailov on Feb 11, 2021
 */
public class CleanseFunctionGroupsConverter
    extends QuadConverter<GetGroupsModelResult, CleanseFunctionGroupRO, CleanseFunctionGroupInfoRO, CleanseFunctionGroup> {

    private static final CleanseFunctionGroupToConverter GROUPS_TO_CONVERTER = new CleanseFunctionGroupToConverter();

    private static final CleanseFunctionGroupFromConverter GROUPS_FROM_CONVERTER = new CleanseFunctionGroupFromConverter();
    /**
     * Constructor.
     * @param from
     * @param to
     */
    public CleanseFunctionGroupsConverter() {
        super(GROUPS_TO_CONVERTER, GROUPS_FROM_CONVERTER);
    }
    /**
     * @author Michael Yashin. Created on 21.05.2015.
     */
    public static class CleanseFunctionGroupToConverter extends Converter<GetGroupsModelResult, CleanseFunctionGroupRO> {

        private static final CompositeCleanseFunctionConverter COMPOSITE_CLEANSE_FUNCTION_CONVERTER
            = new CompositeCleanseFunctionConverter();

        private static final JavaCleanseFunctionConverter JAVA_CLEANSE_FUNCTION_CONVERTER
            = new JavaCleanseFunctionConverter();

        private static final GroovyCleanseFunctionConverter GROOVY_CLEANSE_FUNCTION_CONVERTER
            = new GroovyCleanseFunctionConverter();

        private static final PythonCleanseFunctionConverter PYTHON_CLEANSE_FUNCTION_CONVERTER
            = new PythonCleanseFunctionConverter();

        public CleanseFunctionGroupToConverter() {
            super(CleanseFunctionGroupToConverter::convert, null);
        }

        private static CleanseFunctionGroupRO convert(GetGroupsModelResult group) {

            CleanseFunctionGroupRO root = null;
            for (Entry<String, CleanseFunctionGroup> entry : group.getGroups().entrySet()) {

                CleanseFunctionGroupRO cfg = convert(entry.getValue(), group);
                if (DQUtils.ROOT_FUNCTION_GROUP_NAME.equals(cfg.getName())) {
                    root = cfg;
                }
            }

            return root;
        }

        private static CleanseFunctionGroupRO convert(CleanseFunctionGroup source, GetGroupsModelResult fgr) {

            CleanseFunctionGroupRO result = new CleanseFunctionGroupRO();

            result.setName(source.getName());
            result.setDisplayName(source.getDisplayName());
            result.setDescription(source.getDescription());
            result.setCreateDate(source.getCreateDate());
            result.setCreatedBy(source.getCreatedBy());
            result.setUpdateDate(source.getUpdateDate());
            result.setUpdatedBy(source.getUpdatedBy());

            List<CleanseFunctionGroupRO> groups = new ArrayList<>();
            for (CleanseFunctionGroup element : source.getGroups()) {
                groups.add(convert(element, fgr));
            }

            List<JavaCleanseFunctionRO> javaFunctions = new ArrayList<>();
            List<GroovyCleanseFunctionRO> groovyFunctions = new ArrayList<>();
            List<PythonCleanseFunctionRO> pythonFunctions = new ArrayList<>();
            List<CompositeCleanseFunctionRO> compositeFunctions = new ArrayList<>();
            fgr.getContent(source).forEach(f -> {
                switch (f.getType()) {
                case COMPOSITE:
                    compositeFunctions.add(COMPOSITE_CLEANSE_FUNCTION_CONVERTER.to((CompositeCleanseFunctionSource) f));
                    break;
                case GROOVY:
                    groovyFunctions.add(GROOVY_CLEANSE_FUNCTION_CONVERTER.to((GroovyCleanseFunctionSource) f));
                    break;
                case JAVA:
                    javaFunctions.add(JAVA_CLEANSE_FUNCTION_CONVERTER.to((JavaCleanseFunctionSource) f));
                    break;
                case PYTHON:
                    pythonFunctions.add(PYTHON_CLEANSE_FUNCTION_CONVERTER.to((PythonCleanseFunctionSource) f));
                    break;
                default:
                    break;
                }
            });

            result.setChildren(groups);
            result.setJavaFunctions(javaFunctions);
            result.setCompositeFunctions(compositeFunctions);
            result.setGroovyFunctions(groovyFunctions);
            result.setPythonFunctions(pythonFunctions);

            return result;
        }
    }

    public static class CleanseFunctionGroupFromConverter extends Converter<CleanseFunctionGroupInfoRO, CleanseFunctionGroup> {

        public CleanseFunctionGroupFromConverter() {
            super(CleanseFunctionGroupFromConverter::convert, null);
        }

        private static CleanseFunctionGroup convert(CleanseFunctionGroupInfoRO source) {

            String user = SecurityUtils.getCurrentUserName();
            OffsetDateTime now = OffsetDateTime.now();

            return new CleanseFunctionGroup()
                .withName(source.getName())
                .withDisplayName(source.getDisplayName())
                .withDescription(source.getDescription())
                .withCreateDate(Objects.isNull(source.getCreateDate()) ? now : source.getCreateDate())
                .withCreatedBy(StringUtils.isBlank(source.getCreatedBy()) ? user : source.getCreatedBy())
                .withUpdateDate(Objects.isNull(source.getCreateDate()) ? null : now)
                .withUpdatedBy(StringUtils.isBlank(source.getCreatedBy()) ? null : user)
                .withMappedFunctions(source.getFunctionsNames())
                .withGroups(source.getChildren().stream()
                        .map(CleanseFunctionGroupFromConverter::convert)
                        .collect(Collectors.toList()));
        }
    }
}
